package com.monochromebytes.mononet.test.network;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Timer;
import java.util.TimerTask;

import com.monochromebytes.mononet.stub.Stub;
import com.monochromebytes.mononet.virtual.VirtualAddress;

import com.monochromebytes.mononet.test.Body;

public class ClientNetworkController extends NetworkController {

    private Stub                    server;
    private final VirtualAddress    address;
    private final Timer             timer;

    private SnapshotMessage         snapshot;
    private boolean                 connected;

    private ClientNetworkController self = this;
    private int                     latency;

    public ClientNetworkController(InetSocketAddress localAddress, InetSocketAddress remoteAddress)
            throws IOException {
        super(localAddress);

        this.address = new VirtualAddress(localAddress, remoteAddress, NetworkController.id);

        this.server = socket.getStub(address);
        this.server.addObjectListener(this);

        this.timer = new Timer();
        this.timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    server.send(new PingMessage(), 1000);
                    server.checkForLostObjects();
                    currentNumberOfSentObjects++;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, 1000, 200);

        this.timer.schedule(new TimerTask() {
            @Override
            public void run() {
                long lastCommunication = server.getLastReceivedObjectTimestamp();
                long now = System.currentTimeMillis();
                if (connected && now - lastCommunication > 1000) {
                    System.out.println("Server disconnected: "
                            + server.getAddress().getRemoteAddress());

                    connected = false;

                    server.removeFromSocket();
                    server = socket.getStub(address);
                    server.addObjectListener(self);
                }
            }
        }, 1000, 1000);
    }

    @Override
    public void update(int delta) {
        while (this.server.hasIncomingObjects()) {
            this.server.handleNextObject();
        }
    }

    @Override
    public void receiveObject(Object object, Stub stub) {
        super.receiveObject(object, stub);
        if (!this.connected) {
            System.out.println("Server connected: " + server.getAddress().getRemoteAddress());
            this.connected = true;
        }

        if (object instanceof SnapshotMessage) {
            SnapshotMessage snapshot = (SnapshotMessage) object;

            if (this.snapshot == null || snapshot.getTimestamp() > this.snapshot.getTimestamp()) {
                this.snapshot = snapshot;
            }
        }
    }

    @Override
    public void acknowledgedObject(Object object, Stub stub, int timeToLive, int roundTripTime) {
        super.acknowledgedObject(object, stub, timeToLive, roundTripTime);

        if (object instanceof PingMessage) {
            this.latency += ((roundTripTime * 0.5) - this.latency) * 0.1;
        }
    }

    public Body getBody() {
        if (this.snapshot == null) {
            return null;
        }
        return this.snapshot.getBody();
    }

    public boolean isConnected() {
        return this.connected;
    }

    public int getLatency() {
        return this.latency;
    }
}
